/*
 * kernelxy/trunk/dev_driver.c
 *
 * Harry Wei <harryxiyou@gmail.com> 2012 (C)
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/kernel.h>

/*XXX match correct kernel version for test */
MODULE_LICENSE("GPL");

#define MYCDEV_MAJOR			(231)
#define MYCDEV_SIZE				(1024)

static int mycdev_open(struct inode *inode, struct file *fp) {
	return 0;
}

static int mycdev_release(struct inode *inode, struct file *fp) {
	return 0;
}

static ssize_t mycdev_read(struct file *fp, char __user *buf,
					size_t size, loff_t *pos) {
	unsigned long p = *pos;
	unsigned int count = size;
	char kernel_buf[MYCDEV_SIZE] = "This is mycdev!";

	if (p >= MYCDEV_SIZE) {
		return -1;
	}
	if (count > MYCDEV_SIZE) {
		count = MYCDEV_SIZE - p;
	}
	if (0 != copy_to_user(buf, kernel_buf, count)) {
		printk(KERN_ERR "mycdev_read error!\n");
		return -1;
	}
	printk(KERN_DEBUG "reader: %d bytes was read...\n", count);
	return (ssize_t) count;
}

static ssize_t mycdev_write(struct file *fp, char __user *buf,
					size_t size, loff_t *pos) {
	return (ssize_t) size;
}

static const struct file_operations mycdev_fops = {
	.owner = THIS_MODULE,
	.read = mycdev_read,
	.write = mycdev_write,
	.open = mycdev_open,
	.release = mycdev_release,
};

static int __init mycdev_init(void) {
	int ret = 0;
	printk(KERN_DEBUG "mycdev module is starting...\n");
	ret = register_chrdev(MYCDEV_MAJOR, "mycdev", &mycdev_fops);
	if (0 > ret) {
		printk(KERN_ERR "register failed...\n");
		return -1;
	} else {
		printk(KERN_INFO "register success...\n");
	}
	return 0;
}

static int __exit mycdev_exit(void) {
	printk(KERN_INFO "mycdev module is leaving...\n");
	unregister_chrdev(MYCDEV_MAJOR, "mycdev");
	return 0;
}

module_init(mycdev_init);
module_exit(mycdev_exit);
